home *** CD-ROM | disk | FTP | other *** search
/ Micromanía 92 / CDMM92_1.ISO / SOF 2 SDK / sof2sdk-101.msi / _92D6AC311BB48EBA344BBABC89DA6AB0 / _80DB5D7FCEDA47878CC6C9119B1D6B89 < prev    next >
Encoding:
Text File  |  2002-06-19  |  34.0 KB  |  1,483 lines

  1. // Copyright (C) 2001-2002 Raven Software, Inc.
  2. //
  3. // cg_players.c -- handle the media and animation for player entities
  4.  
  5. #include "cg_local.h"
  6. #include "..\ghoul2\g2.h"
  7. #include "..\game\inv.h"
  8.  
  9. typedef struct SCustomSound
  10. {
  11.     const char    *mGroup;
  12.     const int    mIndex;
  13. } TCustomSound;
  14.  
  15. TCustomSound CustomSounds[MAX_CUSTOM_SOUNDS] = 
  16. {
  17.     { "Death", 0 },
  18.     { "Death", 1 },
  19.     { "Death", 2 },
  20.     { "Pain", 0 },
  21.     { "Pain", 1 },
  22.     { "Pain", 2 },
  23. };
  24.  
  25. /*
  26. ================
  27. CG_CustomSound
  28. ================
  29. */
  30. sfxHandle_t    CG_CustomSound(int clientNum, const char *soundName) 
  31. {
  32.     return trap_S_RegisterSound( soundName );
  33. }
  34.  
  35. /*
  36. ================
  37. CG_CustomPlayerSound
  38. ================
  39. */
  40. sfxHandle_t    CG_CustomPlayerSound(int clientNum, ECustomSounds sound)
  41. {
  42.     clientInfo_t    *ci;
  43.  
  44.     if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) 
  45.     {
  46.         clientNum = 0;
  47.     }
  48.     ci = &cgs.clientinfo[ clientNum ];
  49.  
  50.     return ci->sounds[sound];
  51. }
  52.  
  53.  
  54. /*
  55. ==========================
  56. CG_RegisterClientIdentity
  57. ==========================
  58. */
  59. static qboolean CG_RegisterClientIdentity ( clientInfo_t *ci, const char *identityName ) 
  60. {
  61.     char        afilename[MAX_QPATH];
  62.     TIdentity*    identity;
  63.  
  64.     // Find the identity in question
  65.     identity = BG_FindIdentity( identityName );
  66.     if (!identity )
  67.     {
  68.         return qfalse;
  69.     }
  70.  
  71.     // Register the identity
  72.     ci->ghoul2Model = CG_RegisterIdentity( identity, afilename, &ci->gender );
  73.     if (!ci->ghoul2Model)
  74.     {
  75.         return qfalse;
  76.     }
  77.  
  78.     // find the bolt point for hanging world models of weapons on
  79.     ci->boltWorldWeapon = trap_G2API_AddBolt(ci->ghoul2Model, 0, "rhang_tag_bone");    
  80.  
  81.     if ( !BG_ParseAnimationFile( afilename, ci->animations ) ) 
  82.     {
  83.         Com_Printf( "Failed to load animation file %s\n", afilename );
  84.         trap_G2API_RemoveGhoul2Model(&ci->ghoul2Model, 0);
  85.         return qfalse;
  86.     }
  87.  
  88.     // Flag model as male or female.
  89.     assert(afilename);
  90.     ci->isMale=(strstr(afilename,"female"))?qfalse:qtrue;
  91.     ci->identity = identity;
  92.  
  93.     return qtrue;
  94. }
  95.  
  96. /*
  97. ====================
  98. CG_ColorFromString
  99. ====================
  100. */
  101. static void CG_ColorFromString( const char *v, vec3_t color ) {
  102.     int val;
  103.  
  104.     VectorClear( color );
  105.  
  106.     val = atoi( v );
  107.  
  108.     if ( val < 1 || val > 7 ) {
  109.         VectorSet( color, 1, 1, 1 );
  110.         return;
  111.     }
  112.  
  113.     if ( val & 1 ) {
  114.         color[2] = 1.0f;
  115.     }
  116.     if ( val & 2 ) {
  117.         color[1] = 1.0f;
  118.     }
  119.     if ( val & 4 ) {
  120.         color[0] = 1.0f;
  121.     }
  122. }
  123.  
  124. /*
  125. ===================
  126. CG_LoadClientInfo
  127.  
  128. Load it now, taking the disk hits.
  129. This will usually be deferred to a safe time
  130. ===================
  131. */
  132. static void CG_LoadClientInfo( clientInfo_t *ci ) 
  133. {
  134.     int        i;
  135.     int        modelloaded;
  136.     int        clientNum;
  137.  
  138.     modelloaded = qtrue;
  139.  
  140.     // Initialize all bolt positions
  141.     ci->boltWorldWeapon = -1;
  142.     ci->boltNightvision = -1;
  143.     for ( i = 0; i < MAX_GAMETYPE_ITEMS; i ++ )
  144.     {
  145.         ci->boltGametypeItems[i] = -1;
  146.     }
  147.  
  148.     // Register the client identity
  149.     if ( !CG_RegisterClientIdentity ( ci, ci->identityName ) )
  150.     {
  151.         if ( !CG_RegisterClientIdentity ( ci, DEFAULT_IDENTITY ) )
  152.         {
  153.             Com_Error( ERR_FATAL, "DEFAULT_IDENTITY (%s) failed to register", DEFAULT_IDENTITY );
  154.         }
  155.     }
  156.  
  157.     // sounds
  158.     for( i=0; i< MAX_CUSTOM_SOUNDS; i++ )
  159.     {
  160.         const char* sound;
  161.  
  162.         ci->sounds[i] = 0;
  163.         sound = NULL;
  164.  
  165.         sound = BG_GetModelSound ( ci->identityName, CustomSounds[i].mGroup, CustomSounds[i].mIndex );
  166.  
  167.         // If there is a valid sound to load then load it
  168.         if ( sound && sound[0] )
  169.         {
  170.             ci->sounds[i] = trap_S_RegisterSound( sound );
  171.         }
  172.     }
  173.  
  174.     ci->deferred = qfalse;
  175.  
  176.     // reset any existing players and bodies, because they might be in bad
  177.     // frames for this new model
  178.     clientNum = ci - cgs.clientinfo;
  179.     for ( i = 0 ; i < MAX_GENTITIES ; i++ )
  180.     {
  181.         centity_t* cent;
  182.  
  183.         cent = CG_GetEntity ( i );
  184.  
  185.         if ( cent->currentState.clientNum == clientNum && 
  186.              cent->currentState.eType == ET_PLAYER ) 
  187.         {
  188.             CG_ResetPlayerEntity( cent );
  189.         }
  190.     }
  191. }
  192.  
  193. /*
  194. ======================
  195. CG_CopyClientInfoModel
  196. ======================
  197. */
  198. static void CG_CopyClientInfoModel( clientInfo_t *from, clientInfo_t *to ) 
  199. {
  200.     int i;
  201.  
  202.     to->gender = from->gender;
  203.  
  204.     to->boltWorldWeapon = from->boltWorldWeapon;
  205.  
  206.     for ( i = 0; i < MAX_GAMETYPE_ITEMS; i ++ )
  207.     {
  208.         to->boltGametypeItems[i] = from->boltGametypeItems[i];
  209.     }
  210.  
  211.     to->boltNightvision = from->boltNightvision;
  212.  
  213.     // Copy all the ghoul2 information too
  214.     if (from->ghoul2Model)
  215.     {
  216.         trap_G2API_DuplicateGhoul2Instance( from->ghoul2Model, &to->ghoul2Model );
  217.     }
  218.     else
  219.     {
  220.         assert(0);
  221.         Com_Error(ERR_DROP, "CG_CopyClientInfoModel invalid g2 pointer\n");
  222.     }
  223.  
  224.     memcpy( to->animations, from->animations, sizeof( to->animations ) );
  225.     memcpy( to->sounds, from->sounds, sizeof( to->sounds ) );
  226. }
  227.  
  228. /*
  229. ======================
  230. CG_ScanForExistingClientInfo
  231. ======================
  232. */
  233. static qboolean CG_ScanForExistingClientInfo( clientInfo_t *ci ) 
  234. {
  235.     int                i;
  236.     clientInfo_t    *match;
  237.  
  238.     for ( i = 0 ; i < cgs.maxclients ; i++ ) 
  239.     {
  240.         match = &cgs.clientinfo[ i ];
  241.         
  242.         if ( !match->infoValid ) 
  243.         {
  244.             continue;
  245.         }
  246.         if ( match->deferred ) 
  247.         {
  248.             continue;
  249.         }
  250.  
  251.         // Identity name doesnt match
  252.         if ( Q_stricmp ( ci->identityName, match->identityName ) )
  253.         {
  254.             continue;
  255.         }
  256.  
  257.         if ( ci->team == TEAM_FREE      ||
  258.              ci->team == TEAM_SPECTATOR ||
  259.              ci->team == match->team        )
  260.         {
  261.             // this clientinfo is identical, so use it's handles
  262.             ci->deferred = qfalse;
  263.  
  264.             CG_CopyClientInfoModel( match, ci );
  265.  
  266.             return qtrue;
  267.         }
  268.     }
  269.  
  270.     // nothing matches, so defer the load
  271.     return qfalse;
  272. }
  273.  
  274. /*
  275. ======================
  276. CG_SetDeferredClientInfo
  277.  
  278. We aren't going to load it now, so grab some other
  279. client's info to use until we have some spare time.
  280. ======================
  281. */
  282. static void CG_SetDeferredClientInfo( clientInfo_t *ci ) 
  283. {
  284.     int                i;
  285.     clientInfo_t    *match;
  286.  
  287.     // spectators can use any skin 
  288.     if ( ci->team == TEAM_SPECTATOR )
  289.     {
  290.         for ( i = 0; i < cgs.maxclients; i ++ )
  291.         {
  292.             match = &cgs.clientinfo[ i ];
  293.             if ( !match->infoValid ) 
  294.             {
  295.                 continue;
  296.             }
  297.  
  298.             ci->deferred = qtrue;
  299.             CG_CopyClientInfoModel( match, ci );
  300.             return;
  301.         }
  302.     }
  303.  
  304.     // Try to use any skin from the team they are joining
  305.     if ( cgs.gametypeData->teams )
  306.     {
  307.         for ( i = 0; i < cgs.maxclients; i ++ )
  308.         {
  309.             match = &cgs.clientinfo[ i ];
  310.             if ( !match->infoValid ) 
  311.             {
  312.                 continue;
  313.             }
  314.  
  315.             // Must be on the same team
  316.             if ( match->team != ci->team )
  317.             {
  318.                 continue;
  319.             }
  320.  
  321.             ci->deferred = qtrue;
  322.             CG_CopyClientInfoModel( match, ci );
  323.             return;
  324.         }
  325.     }
  326.     // find the first valid clientinfo and grab its stuff
  327.     else
  328.     {
  329.         for ( i = 0 ; i < cgs.maxclients ; i++ ) 
  330.         {
  331.             match = &cgs.clientinfo[ i ];
  332.             if ( !match->infoValid ) 
  333.             {
  334.                 continue;
  335.             }
  336.  
  337.             ci->deferred = qtrue;
  338.             CG_CopyClientInfoModel( match, ci );
  339.             return;
  340.         }
  341.     }
  342.  
  343.     CG_LoadClientInfo( ci );
  344. }
  345.  
  346. /*
  347. ======================
  348. CG_ResetClientIdentity
  349. ======================
  350. */
  351. void CG_ResetClientIdentity ( int clientNum )
  352. {
  353.     clientInfo_t *ci;
  354.     centity_t     *cent;
  355.     int             i;
  356.  
  357.     ci = &cgs.clientinfo[clientNum];
  358.     cent = CG_GetEntity ( clientNum );    
  359.  
  360.     // Clean up the raw ghoul model
  361.     if ( ci->ghoul2Model )
  362.     {
  363.         trap_G2API_CleanGhoul2Models ( &ci->ghoul2Model );
  364.         ci->ghoul2Model = NULL;
  365.     }
  366.  
  367.     // Clean up the active ghoul model
  368.     if ( cent->ghoul2 )
  369.     {
  370.         trap_G2API_CleanGhoul2Models ( ¢->ghoul2 );
  371.         cent->pe.weaponModelSpot = 0;
  372.         cent->pe.weapon = 0;
  373.         cent->ghoul2 = NULL;
  374.     }
  375.  
  376.     // Reset all bolt position
  377.     ci->boltWorldWeapon = -1;
  378.     ci->boltNightvision = -1;
  379.     for ( i = 0; i < MAX_GAMETYPE_ITEMS; i ++ )
  380.     {
  381.         ci->boltGametypeItems[i] = -1;
  382.     }
  383. }
  384.  
  385. /*
  386. ======================
  387. CG_NewClientInfo
  388. ======================
  389. */
  390. void CG_NewClientInfo( int clientNum ) 
  391. {
  392.     clientInfo_t *ci;
  393.     clientInfo_t newInfo;
  394.     centity_t*     cent;
  395.     const char    *configstring;
  396.     const char    *v;
  397.  
  398.     ci = &cgs.clientinfo[clientNum];
  399.  
  400.     cent = CG_GetEntity ( clientNum );    
  401.  
  402.     configstring = CG_ConfigString( clientNum + CS_PLAYERS );
  403.     if ( !configstring[0] ) 
  404.     {
  405.         memset( ci, 0, sizeof( *ci ) );
  406.         return;
  407.     }
  408.  
  409.     // Clear the client models / etc
  410.     CG_ResetClientIdentity ( clientNum );
  411.  
  412.     ci->infoValid = qfalse;
  413.  
  414.     // build into a temp buffer so the defer checks can use
  415.     // the old value
  416.     memset( &newInfo, 0, sizeof( newInfo ) );
  417.  
  418.     // isolate the player's name
  419.     v = Info_ValueForKey(configstring, "n");
  420.     Q_strncpyz( newInfo.name, v, sizeof( newInfo.name ) );
  421.  
  422.     // bot skill
  423.     v = Info_ValueForKey( configstring, "skill" );
  424.     newInfo.botSkill = atoi( v );
  425.  
  426.     // handicap
  427.     v = Info_ValueForKey( configstring, "hc" );
  428.     newInfo.handicap = atoi( v );
  429.  
  430.     // wins
  431.     v = Info_ValueForKey( configstring, "w" );
  432.     newInfo.wins = atoi( v );
  433.  
  434.     // losses
  435.     v = Info_ValueForKey( configstring, "l" );
  436.     newInfo.losses = atoi( v );
  437.  
  438.     // team
  439.     v = Info_ValueForKey( configstring, "t" );
  440.     newInfo.team = atoi( v );
  441.  
  442.     // team task
  443.     v = Info_ValueForKey( configstring, "tt" );
  444.     newInfo.teamTask = atoi(v);
  445.  
  446.     // team leader
  447.     v = Info_ValueForKey( configstring, "tl" );
  448.     newInfo.teamLeader = atoi(v);
  449.  
  450.     // identity
  451.     v = Info_ValueForKey( configstring, "identity" );
  452.     Q_strncpyz ( newInfo.identityName, v, sizeof(newInfo.identityName) );
  453.  
  454.     // Inform the ui of the team switch
  455.     if ( clientNum == cg.clientNum )
  456.     {
  457.         char        temp[MAX_QPATH];
  458.         const char* identityCvar;
  459.  
  460.         trap_Cvar_Set ( "ui_info_team", va("%i",newInfo.team ) );
  461.  
  462.         if (cgs.gametypeData->teams )
  463.         {
  464.             identityCvar = "team_identity";
  465.         }
  466.         else
  467.         {
  468.             identityCvar = "identity";
  469.         }
  470.  
  471.         trap_Cvar_VariableStringBuffer ( identityCvar, temp, sizeof(temp) );
  472.  
  473.         if ( Q_stricmp ( temp, newInfo.identityName ) )
  474.         {
  475.             trap_Cvar_Set ( identityCvar, newInfo.identityName );
  476.         }
  477.     }
  478.  
  479.     // scan for an existing clientinfo that matches this modelname
  480.     // so we can avoid loading checks if possible
  481.     if ( !CG_ScanForExistingClientInfo( &newInfo ) ) 
  482.     {
  483.         qboolean    forceDefer;
  484.  
  485.         forceDefer = trap_MemoryRemaining() < 4000000;
  486.  
  487.         // if we are defering loads, just have it pick the first valid
  488.         if ( forceDefer || (cg_deferPlayers.integer && !cg_buildScript.integer && !cg.loading ) ) 
  489.         {
  490.             // keep whatever they had if it won't violate team skins
  491.             CG_SetDeferredClientInfo( &newInfo );
  492.  
  493.             // if we are low on memory, leave them with this model
  494.             if ( forceDefer ) 
  495.             {
  496.                 Com_Printf( "Memory is low.  Using deferred model.\n" );
  497.                 newInfo.deferred = qfalse;
  498.             }
  499.         } 
  500.         else 
  501.         {
  502.             CG_LoadClientInfo( &newInfo );
  503.         }
  504.     }
  505.  
  506.     // replace whatever was there with the new one
  507.     newInfo.infoValid = qtrue;
  508.     *ci = newInfo;
  509. }
  510.  
  511. /*
  512. ======================
  513. CG_LoadDeferredPlayers
  514.  
  515. Called each frame when a player is dead
  516. and the scoreboard is up
  517. so deferred players can be loaded
  518. ======================
  519. */
  520. void CG_LoadDeferredPlayers( void ) 
  521. {
  522.     int                i;
  523.     clientInfo_t    *ci;
  524.  
  525.     // scan for a deferred player to load
  526.     for ( i = 0, ci = cgs.clientinfo ; i < cgs.maxclients ; i++, ci++ ) 
  527.     {
  528.         if ( ci->infoValid && ci->deferred ) 
  529.         {
  530.             // if we are low on memory, leave it deferred
  531.             if ( trap_MemoryRemaining() < 4000000 ) 
  532.             {
  533.                 Com_Printf( "Memory is low.  Using deferred model.\n" );
  534.                 ci->deferred = qfalse;
  535.                 continue;
  536.             }
  537.  
  538.             // Clear the client models / etc
  539.             CG_ResetClientIdentity ( i );
  540.  
  541.             CG_LoadClientInfo( ci );
  542.         }
  543.     }
  544. }
  545.  
  546. /*
  547. =================
  548. CG_AddPainTwitch
  549. =================
  550. */
  551. static void CG_AddPainTwitch( centity_t *cent, vec3_t torsoAngles ) 
  552. {
  553.     int        t;
  554.     float    f;
  555.  
  556.     t = cg.time - cent->pe.painTime;
  557.     if ( t >= PAIN_TWITCH_TIME ) {
  558.         return;
  559.     }
  560.  
  561.     f = 1.0 - (float)t / PAIN_TWITCH_TIME;
  562.  
  563.     if ( cent->pe.painDirection ) {
  564.         torsoAngles[ROLL] += 20 * f;
  565.     } else {
  566.         torsoAngles[ROLL] -= 20 * f;
  567.     }
  568. }
  569.  
  570. /*
  571. ===============
  572. CG_PlayerFloatSprite
  573.  
  574. Float a sprite over the player's head
  575. ===============
  576. */
  577. static void CG_PlayerFloatSprite( centity_t *cent, qhandle_t shader ) 
  578. {
  579.     int                rf;
  580.     refEntity_t        ent;
  581.  
  582.     if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) 
  583.     {
  584.         // only show in mirrors
  585.         rf = RF_THIRD_PERSON;        
  586.     } 
  587.     else 
  588.     {
  589.         rf = 0;
  590.     }
  591.  
  592.     memset( &ent, 0, sizeof( ent ) );
  593.     VectorCopy( cent->lerpOrigin, ent.origin );
  594.     ent.origin[2] += 58;
  595.     ent.reType = RT_SPRITE;
  596.     ent.customShader = shader;
  597.     ent.radius = 10;
  598.     ent.renderfx = rf;
  599.     ent.shaderRGBA[0] = 255;
  600.     ent.shaderRGBA[1] = 255;
  601.     ent.shaderRGBA[2] = 255;
  602.     ent.shaderRGBA[3] = 255;
  603.     trap_R_AddRefEntityToScene( &ent );
  604. }
  605.  
  606. /*
  607. ===============
  608. CG_PlayerSprites
  609.  
  610. Float sprites over the player's head
  611. ===============
  612. */
  613. static void CG_PlayerSprites( centity_t *cent ) 
  614. {
  615.     int        team;
  616.  
  617.     team = cgs.clientinfo[ cent->currentState.clientNum ].team;
  618.     if ( !(cent->currentState.number == cg.snap->ps.clientNum) &&
  619.          !(cent->currentState.eFlags & EF_DEAD)     && 
  620.           cg.snap->ps.persistant[PERS_TEAM] == team &&
  621.           cgs.gametypeData->teams ) 
  622.     {
  623.         if (cg_drawFriend.integer) 
  624.         {
  625.             CG_PlayerFloatSprite( cent, team==TEAM_RED?cgs.media.redFriendShader:cgs.media.blueFriendShader );
  626.         }
  627.  
  628.         return;
  629.     }
  630. }
  631.  
  632. /*
  633. ===============
  634. CG_PlayerShadow
  635.  
  636. Returns the Z component of the surface being shadowed
  637.  
  638.   should it return a full plane instead of a Z?
  639. ===============
  640. */
  641. #define    SHADOW_DISTANCE        256
  642. qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane ) 
  643. {
  644.     vec3_t        end, mins = {-15, -15, 0}, maxs = {15, 15, 2};
  645.     trace_t        trace;
  646.     float        alpha;
  647.  
  648.     *shadowPlane = 0;
  649.  
  650.     if ( cg_shadows.integer == 0 ) {
  651.         return qfalse;
  652.     }
  653.  
  654.     // send a trace down from the player to the ground
  655.     VectorCopy( cent->lerpOrigin, end );
  656.     end[2] -= SHADOW_DISTANCE;
  657.  
  658.     trap_CM_BoxTrace( &trace, cent->lerpOrigin, end, mins, maxs, 0, MASK_PLAYERSOLID );
  659.  
  660.     // no shadow if too high
  661.     if ( trace.fraction == 1.0 || trace.startsolid || trace.allsolid ) {
  662.         return qfalse;
  663.     }
  664.  
  665.     *shadowPlane = trace.endpos[2] + 1;
  666.  
  667.     if ( cg_shadows.integer != 1 ) {    // no mark for stencil or projection shadows
  668.         return qtrue;
  669.     }
  670.  
  671.     // fade the shadow out with height
  672.     alpha = 1.0 - trace.fraction;
  673.     alpha = 1.0;
  674.     trap_R_AddDecalToScene ( cgs.media.shadowMarkShader, trace.endpos, trace.plane.normal, 
  675.                              cent->currentState.apos.trBase[YAW], 1.0,0.0,1.0,1.0, qfalse, 24, qtrue );
  676.  
  677.     return qtrue;
  678. }
  679.  
  680.  
  681. /*
  682. ===============
  683. CG_PlayerSplash
  684.  
  685. Draw a mark at the water surface
  686. ===============
  687. */
  688. static void CG_PlayerSplash( centity_t *cent ) {
  689.     vec3_t        start, end;
  690.     trace_t        trace;
  691.     int            contents;
  692.     polyVert_t    verts[4];
  693.  
  694.     if ( !cg_shadows.integer ) {
  695.         return;
  696.     }
  697.  
  698.     VectorCopy( cent->lerpOrigin, end );
  699.     end[2] -= 24;
  700.  
  701.     // if the feet aren't in liquid, don't make a mark
  702.     // this won't handle moving water brushes, but they wouldn't draw right anyway...
  703.     contents = trap_CM_PointContents( end, 0 );
  704.     if ( !( contents & ( CONTENTS_WATER | CONTENTS_LAVA ) ) ) {
  705.         return;
  706.     }
  707.  
  708.     VectorCopy( cent->lerpOrigin, start );
  709.     start[2] += 32;
  710.  
  711.     // if the head isn't out of liquid, don't make a mark
  712.     contents = trap_CM_PointContents( start, 0 );
  713.     if ( contents & ( CONTENTS_SOLID | CONTENTS_WATER | CONTENTS_LAVA ) ) {
  714.         return;
  715.     }
  716.  
  717.     // trace down to find the surface
  718.     trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, ( CONTENTS_WATER | CONTENTS_LAVA ) );
  719.  
  720.     if ( trace.fraction == 1.0 ) {
  721.         return;
  722.     }
  723.  
  724.     // create a mark polygon
  725.     VectorCopy( trace.endpos, verts[0].xyz );
  726.     verts[0].xyz[0] -= 32;
  727.     verts[0].xyz[1] -= 32;
  728.     verts[0].st[0] = 0;
  729.     verts[0].st[1] = 0;
  730.     verts[0].modulate[0] = 255;
  731.     verts[0].modulate[1] = 255;
  732.     verts[0].modulate[2] = 255;
  733.     verts[0].modulate[3] = 255;
  734.  
  735.     VectorCopy( trace.endpos, verts[1].xyz );
  736.     verts[1].xyz[0] -= 32;
  737.     verts[1].xyz[1] += 32;
  738.     verts[1].st[0] = 0;
  739.     verts[1].st[1] = 1;
  740.     verts[1].modulate[0] = 255;
  741.     verts[1].modulate[1] = 255;
  742.     verts[1].modulate[2] = 255;
  743.     verts[1].modulate[3] = 255;
  744.  
  745.     VectorCopy( trace.endpos, verts[2].xyz );
  746.     verts[2].xyz[0] += 32;
  747.     verts[2].xyz[1] += 32;
  748.     verts[2].st[0] = 1;
  749.     verts[2].st[1] = 1;
  750.     verts[2].modulate[0] = 255;
  751.     verts[2].modulate[1] = 255;
  752.     verts[2].modulate[2] = 255;
  753.     verts[2].modulate[3] = 255;
  754.  
  755.     VectorCopy( trace.endpos, verts[3].xyz );
  756.     verts[3].xyz[0] += 32;
  757.     verts[3].xyz[1] -= 32;
  758.     verts[3].st[0] = 1;
  759.     verts[3].st[1] = 0;
  760.     verts[3].modulate[0] = 255;
  761.     verts[3].modulate[1] = 255;
  762.     verts[3].modulate[2] = 255;
  763.     verts[3].modulate[3] = 255;
  764.  
  765.     trap_R_AddPolyToScene( cgs.media.wakeMarkShader, 4, verts );
  766. }
  767.  
  768. /*
  769. ===============
  770. CG_UpdatePlayerAnimations
  771. ===============
  772. */
  773. static void CG_UpdatePlayerAnimations (    centity_t* cent    ) 
  774. {
  775.     clientInfo_t *ci;
  776.  
  777.     // Make sure its a player
  778.     assert ( cent->currentState.eType == ET_PLAYER );
  779.  
  780.     // Retrieve the client info pointer for the given client
  781.     ci = &cgs.clientinfo[ cent->currentState.clientNum ];
  782.  
  783.     // Update the leg animation
  784.     if ( cent->pe.legs.anim != cent->currentState.legsAnim && cent->currentState.legsAnim != -1 )
  785.     {
  786.         animation_t    *anim;
  787.         int            flags;
  788.         float        speed;
  789.  
  790.         anim = &ci->animations[ (cent->currentState.legsAnim & (~ANIM_TOGGLEBIT)) ];
  791.  
  792.         if ( anim->loopFrames == -1 )
  793.         {
  794.             flags = BONE_ANIM_OVERRIDE|BONE_ANIM_OVERRIDE_FREEZE;
  795.         }
  796.         else
  797.         {
  798.             flags = BONE_ANIM_OVERRIDE_LOOP;
  799.         }
  800.  
  801.         if (cg_animBlend.integer)
  802.         {
  803.             flags |= BONE_ANIM_BLEND;
  804.         }
  805.  
  806.         speed = 1.0f;
  807.  
  808.         trap_G2API_SetBoneAnim(cent->ghoul2, 0, "model_root", anim->firstFrame, anim->firstFrame + anim->numFrames, flags, 50.0f / anim->frameLerp * speed, cg.time, -1, 150);
  809.  
  810.         cent->pe.legs.anim = cent->currentState.legsAnim;
  811.         cent->pe.legs.animTime = cg.time;
  812.     }
  813.  
  814.     // Update the torso animation
  815.     if ( cent->pe.torso.anim != cent->currentState.torsoAnim && cent->currentState.torsoAnim != -1 )
  816.     {
  817.         animation_t    *anim;
  818.         int            flags;
  819.         float        time;
  820.         anim = &ci->animations[ (cent->currentState.torsoAnim & (~ANIM_TOGGLEBIT)) ];
  821.  
  822.         if ( anim->loopFrames == -1 )
  823.         {
  824.             flags = BONE_ANIM_OVERRIDE|BONE_ANIM_OVERRIDE_FREEZE;
  825.         }
  826.         else
  827.         {
  828.             flags = BONE_ANIM_OVERRIDE_LOOP;
  829.         }
  830.  
  831.         if (cg_animBlend.integer)
  832.         {
  833.             flags |= BONE_ANIM_BLEND;
  834.         }
  835.  
  836.         if ( cent->currentState.torsoTimer > 0 )
  837.         {
  838.             time = cent->currentState.torsoTimer;
  839.             time /= anim->numFrames;
  840.         }
  841.         else
  842.         {
  843.             time = anim->frameLerp;
  844.         }
  845.  
  846.         // Default to 20 FPS.
  847.         if ( time <= 0 )
  848.         {
  849.             time = 1000.0f / 20.0f;
  850.         }
  851.         
  852.         trap_G2API_SetBoneAnim(cent->ghoul2, 0, "lower_lumbar", anim->firstFrame, anim->firstFrame + anim->numFrames, flags, 50.0f / time, cg.time, -1, 150);
  853.  
  854.         cent->pe.torso.anim = cent->currentState.torsoAnim ;
  855.         cent->pe.torso.animTime = cg.time;
  856.     }
  857. }
  858.  
  859. /*
  860. ===============
  861. CG_PlayerGametypeItems
  862. ===============
  863. */
  864. void CG_PlayerGametypeItems ( centity_t* cent, vec3_t angles )
  865. {
  866.     clientInfo_t    *ci = &cgs.clientinfo[cent->currentState.clientNum];
  867.     int                i;
  868.     int                rf;
  869.  
  870.     if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) 
  871.     {
  872.         // only show in mirrors
  873.         rf = RF_THIRD_PERSON;        
  874.     } 
  875.     else 
  876.     {
  877.         rf = 0;
  878.     }
  879.  
  880.     // If the player is wearing goggles then draw them on their head
  881.     if ( cent->currentState.eFlags & EF_GOGGLES )
  882.     {
  883.         refEntity_t    item;
  884.  
  885.         if ( ci->boltNightvision == -1 )
  886.         {
  887.             ci->boltNightvision = trap_G2API_AddBolt( cent->ghoul2, 0, "*head_t" );
  888.         }
  889.  
  890.         memset ( &item, 0, sizeof(item) );
  891.         item.renderfx = rf | RF_MINLIGHT;
  892.         item.ghoul2 = cgs.media.nightVisionModel;
  893.         G2_PositionEntityOnBolt ( &item, cent->ghoul2, 0, ci->boltNightvision, cent->lerpOrigin, angles, cent->modelScale );
  894.         trap_R_AddRefEntityToScene(&item);
  895.     }
  896.         
  897.  
  898.     // Loop through the gametye items  and handle each separately
  899.     for ( i = 0; i < MAX_GAMETYPE_ITEMS; i ++ )
  900.     {
  901.         // If they dont have the item then skip it 
  902.         if ( !(cent->currentState.gametypeitems & (1<<i) ))
  903.         {
  904.             continue;
  905.         }
  906.  
  907.         // Need to add bolt to model
  908.         if ( cg_items[ MODELINDEX_GAMETYPE_ITEM + i ].boltModel )
  909.         {
  910.             refEntity_t    item;
  911.  
  912.             if ( ci->boltGametypeItems[i] == -1 )
  913.             {
  914.                 ci->boltGametypeItems[i] = trap_G2API_AddBolt( cent->ghoul2, 0, "*back" );    
  915.                 if ( ci->boltGametypeItems[i] == -1 )
  916.                 {
  917.                     continue;
  918.                 }
  919.  
  920.                 // Remove all identity items on the back
  921.                 CG_RemoveIdentityItemsOnBack ( cent );
  922.             }
  923.  
  924.             memset ( &item, 0, sizeof(item) );
  925.             item.renderfx = rf | RF_MINLIGHT;
  926.             item.ghoul2 = cg_items[ MODELINDEX_GAMETYPE_ITEM + i ].boltModel;
  927.             G2_PositionEntityOnBolt ( &item, cent->ghoul2, 0, ci->boltGametypeItems[i], cent->lerpOrigin, angles, cent->modelScale );
  928.             trap_R_AddRefEntityToScene(&item);
  929.         }
  930.     }    
  931. }
  932.  
  933. /*
  934. ===============
  935. CG_UpdatePlayerWeaponModel
  936. ===============
  937. */
  938. void CG_UpdatePlayerWeaponModel ( centity_t* cent, qboolean force )
  939. {
  940.     clientInfo_t *ci = &cgs.clientinfo[cent->currentState.clientNum];
  941.     void         *model;
  942.     
  943.     model = NULL;
  944.     if ( (cent->pe.torso.anim&~ANIM_TOGGLEBIT) == TORSO_USE )
  945.     {
  946.         int item;
  947.  
  948.         cent->pe.weapon = -1;
  949.  
  950.         for ( item = 0; item < MAX_GAMETYPE_ITEMS; item ++ )
  951.         {
  952.             if ( cent->currentState.gametypeitems & (1<<item) )
  953.             {
  954.                 model = cg_items[MODELINDEX_GAMETYPE_ITEM+item].useModel;
  955.                 break;
  956.             }
  957.         }
  958.     }
  959.     // If the weapon model hasnt changed then dont update it
  960.     else if ( !force && cent->currentState.weapon == cent->pe.weapon )
  961.     {
  962.         return;
  963.     }
  964.     else
  965.     {
  966.         // Ensure the weapon is registered
  967.         CG_RegisterWeapon ( cent->currentState.weapon );
  968.  
  969.         model = cg_weapons[cent->currentState.weapon].weaponG2Model;
  970.  
  971.         cent->pe.weapon = cent->currentState.weapon;
  972.     }
  973.  
  974.     // Get rid of whats in their hand
  975.     if ( cent->pe.weaponModelSpot )
  976.     {
  977.         trap_G2API_DetachG2Model ( cent->ghoul2, cent->pe.weaponModelSpot );
  978.         trap_G2API_RemoveGhoul2Model( ¢->ghoul2, cent->pe.weaponModelSpot );
  979.         cent->pe.weaponModelSpot = 0;
  980.     }
  981.  
  982.     // If we have a ghoul model then we can attach it to the right hand of the player model
  983.     if (model)
  984.     {
  985.         cent->pe.weaponModelSpot = trap_G2API_CopySpecificGhoul2Model(model, 0, cent->ghoul2, -1 ); 
  986.         trap_G2API_SetBoltInfo(cent->ghoul2, cent->pe.weaponModelSpot, ci->boltWorldWeapon );
  987.         trap_G2API_AttachG2Model(cent->ghoul2, cent->pe.weaponModelSpot, cent->ghoul2, ci->boltWorldWeapon, 0);
  988.  
  989.         // Special case to make sure the bayonet shows up for the ak74
  990.         switch ( cent->currentState.weapon )
  991.         {
  992.             case WP_AK74_ASSAULT_RIFLE:
  993.                 trap_G2API_SetSurfaceOnOff( cent->ghoul2, cent->pe.weaponModelSpot, "bayonet_off", 0 );
  994.                 break;
  995.  
  996.             case WP_M4_ASSAULT_RIFLE:
  997.                 trap_G2API_SetSurfaceOnOff( cent->ghoul2, cent->pe.weaponModelSpot, "m203_off", 0 );
  998.                 break;
  999.         }
  1000.     }
  1001. }
  1002.  
  1003. /*
  1004. ===============
  1005. CG_UpdatePlayerModel
  1006. ===============
  1007. */
  1008. void CG_UpdatePlayerModel ( centity_t* cent )
  1009. {
  1010.     if ((cent->ghoul2 == NULL) && trap_G2_HaveWeGhoul2Models(cgs.clientinfo[cent->currentState.clientNum].ghoul2Model))
  1011.     {
  1012.         CG_ResetPlayerEntity ( cent );
  1013.  
  1014.         if (!cgs.clientinfo[cent->currentState.clientNum].ghoul2Model)
  1015.         {
  1016.             Com_Error(ERR_DROP, "CG_UpdatePlayerModel invalid g2 pointer for client\n");
  1017.         }
  1018.         trap_G2API_DuplicateGhoul2Instance(cgs.clientinfo[cent->currentState.clientNum].ghoul2Model, ¢->ghoul2);
  1019.     
  1020.         // Force the players weapon model to be updated since their main model changed
  1021.         CG_UpdatePlayerWeaponModel ( cent, qtrue );
  1022.     }
  1023. }
  1024.  
  1025. /*
  1026. ===============
  1027. CG_Player
  1028. ===============
  1029. */
  1030. void CG_Player( centity_t *cent ) 
  1031. {
  1032.     clientInfo_t    *ci;
  1033.     refEntity_t        player;
  1034.     int                clientNum;
  1035.     int                renderfx;
  1036.     qboolean        shadow;
  1037.     float            shadowPlane;
  1038.     int                iwantout = 0;
  1039.     vec3_t            save;
  1040.  
  1041.     // Dont draw any player models when the round has ended
  1042.     if ( cg.snap->ps.pm_type == PM_INTERMISSION )
  1043.     {
  1044.         return;
  1045.     }
  1046.  
  1047.     // the client number is stored in clientNum.  It can't be derived
  1048.     // from the entity number, because a single client may have
  1049.     // multiple corpses on the level using the same clientinfo
  1050.     clientNum = cent->currentState.clientNum;
  1051.     if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) 
  1052.     {
  1053.         Com_Error( ERR_FATAL, "Bad clientNum on player entity");
  1054.     }
  1055.  
  1056.     ci = &cgs.clientinfo[ clientNum ];
  1057.  
  1058.     // it is possible to see corpses from disconnected players that may
  1059.     // not have valid clientinfo
  1060.     if ( !ci->infoValid ) 
  1061.     {
  1062.         return;
  1063.     }
  1064.  
  1065.     // don't draw the death sequences, as the body queue should be doing it for us
  1066.     if ( cent->currentState.eFlags & EF_DEAD )
  1067.     {    
  1068.         return;
  1069.     }
  1070.  
  1071.     // Add the player to the radar if on the same team and its a team game
  1072.     if ( cgs.gametypeData->teams )
  1073.     {
  1074.         if ( cg.snap->ps.clientNum != cent->currentState.number && ci->team == cgs.clientinfo[ cg.snap->ps.clientNum ].team )
  1075.         {
  1076.             cg.radarEntities[cg.radarEntityCount++] = cent;
  1077.         }
  1078.     }
  1079.  
  1080.     // get the player model information
  1081.     renderfx = 0;
  1082.     if ( cent->currentState.number == cg.snap->ps.clientNum) 
  1083.     {
  1084.         // If rendering third person then we shouldnt draw the player if 
  1085.         // the view origin is too close to the player (ie, inside it)
  1086.         if ( cg.renderingThirdPerson )
  1087.         {
  1088.             vec3_t diff;
  1089.             vec3_t a;
  1090.             vec3_t b;
  1091.             float f;
  1092.  
  1093.             VectorCopy ( cg.refdef.vieworg, a );
  1094.             VectorCopy ( cent->lerpOrigin, b );
  1095. //            a[2] = 0;
  1096. //            b[2] = 0;
  1097.  
  1098.             VectorSubtract ( a, b, diff );
  1099.  
  1100.             f = VectorLengthSquared ( diff );
  1101.             if ( VectorLengthSquared ( diff ) < 1800 )
  1102.             {
  1103.                 renderfx = RF_THIRD_PERSON;
  1104.             }
  1105.         }
  1106.         // only draw in mirrors
  1107.         else
  1108.         {
  1109.             renderfx = RF_THIRD_PERSON;            
  1110.         } 
  1111.     }
  1112.  
  1113.     // Initialize the ref entity
  1114.     memset (&player, 0, sizeof(player));
  1115.  
  1116.     // NOTENOTE Temporary
  1117.     VectorSet(player.modelScale, 1,1,1);
  1118.     player.radius = 64;
  1119.     VectorClear(player.angles);
  1120.  
  1121.     // add the shadow
  1122.     shadow = CG_PlayerShadow( cent, &shadowPlane );
  1123.  
  1124.     if (iwantout)
  1125.     {
  1126.         return;
  1127.     }
  1128.  
  1129.     // add a water splash if partially in and out of water
  1130. //    CG_PlayerSplash( cent );
  1131.  
  1132.     if ( cg_shadows.integer == 3 && shadow ) 
  1133.     {
  1134.         renderfx |= RF_SHADOW_PLANE;
  1135.     }
  1136.  
  1137.     // use the same origin for all
  1138.     renderfx |= RF_LIGHTING_ORIGIN | RF_MINLIGHT;            
  1139.  
  1140.     VectorCopy( cent->lerpOrigin, player.origin );
  1141.     VectorCopy( cent->lerpOrigin, player.lightingOrigin );
  1142.  
  1143.     player.shadowPlane = shadowPlane;
  1144.     player.renderfx = renderfx;
  1145.     
  1146.     // don't positionally lerp at all
  1147.     VectorCopy (player.origin, player.oldorigin);    
  1148.  
  1149.     // get the animation state (after rotation, to allow feet shuffle)
  1150.     CG_UpdatePlayerModel ( cent );
  1151.     CG_UpdatePlayerAnimations ( cent );
  1152.     CG_UpdatePlayerWeaponModel ( cent, qfalse );
  1153.  
  1154.     if ( (cent->currentState.eFlags & EF_DEAD ) )
  1155.     {
  1156.         AnglesToAxis( cent->lerpAngles, player.axis );
  1157.     }
  1158.     else
  1159.     {
  1160.         // Force the legs and torso to stay aligned for now to ensure the client
  1161.         // and server are in sync with the angles.  
  1162.         // TODO: Come up with a way to keep these in sync on both client and server
  1163.         cent->pe.torso.yawing = qtrue;
  1164.         cent->pe.torso.pitching = qtrue;
  1165.         cent->pe.legs.yawing= qtrue;
  1166.  
  1167.         BG_PlayerAngles( cent->lerpAngles, 
  1168.                           player.axis, 
  1169.                          
  1170.                          cent->pe.ghoulLegsAngles, 
  1171.                          cent->pe.ghoulLowerTorsoAngles,
  1172.                          cent->pe.ghoulUpperTorsoAngles,
  1173.                          cent->pe.ghoulHeadAngles,
  1174.  
  1175.                          cent->lerpLeanOffset,
  1176.     
  1177.                          cent->pe.painTime, 
  1178.                          cent->pe.painDirection, 
  1179.                          cg.time,
  1180.  
  1181.                          ¢->pe.torso,
  1182.                          ¢->pe.legs,
  1183.  
  1184.                          cg.frametime, 
  1185.                          cent->lerpVelocity,
  1186.                          (cent->currentState.eFlags & EF_DEAD), 
  1187.                          cent->currentState.angles2[YAW],
  1188.                          cent->ghoul2 );
  1189.     }
  1190.  
  1191.     VectorCopy ( cent->lerpOrigin, save );
  1192.     VectorCopy ( player.origin, cent->lerpOrigin );
  1193.  
  1194.     CG_ScaleModelAxis(&player);
  1195.  
  1196.     // Copy the ghoul 2 info into the ref entity
  1197.     CG_SetGhoul2Info(&player, cent);
  1198.  
  1199.     // Now add the player to the scene
  1200.     trap_R_AddRefEntityToScene(&player);
  1201.     
  1202.     //
  1203.     // add the gun / barrel / flash
  1204.     //
  1205.     // need the angle AFTER the lean is added
  1206. //    vectoangles( player.axis[0], cent->pe.ghoulRootAngles );     
  1207.  
  1208.     // Render any weapon related effects
  1209.     CG_PlayerWeaponEffects ( &player, cent, ci->team, cent->pe.ghoulLegsAngles ); 
  1210.     
  1211.     // Render any of the floating sprites above the players head
  1212.     CG_PlayerSprites ( cent );
  1213.  
  1214.     CG_PlayerGametypeItems ( cent, cent->pe.ghoulLegsAngles );
  1215.  
  1216.     VectorCopy ( save, cent->lerpOrigin );
  1217. }
  1218.  
  1219. /*
  1220. ===============
  1221. CG_ResetPlayerEntity
  1222.  
  1223. A player just came into view or teleported, so reset all animation info
  1224. ===============
  1225. */
  1226. void CG_ResetPlayerEntity( centity_t *cent ) 
  1227. {
  1228.     cent->errorTime = -99999;        // guarantee no error decay added
  1229.     cent->extrapolated = qfalse;    
  1230.  
  1231.     BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin );
  1232.     BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles );
  1233.  
  1234.     VectorCopy( cent->lerpOrigin, cent->rawOrigin );
  1235.     VectorCopy( cent->lerpAngles, cent->rawAngles );
  1236.  
  1237.     cent->pe.legs.anim = -1;
  1238.     cent->pe.torso.anim = -1;
  1239.  
  1240.     memset( ¢->pe.legs, 0, sizeof( cent->pe.legs ) );
  1241.     cent->pe.legs.yawAngle = cent->rawAngles[YAW];
  1242.     cent->pe.legs.yawing = qfalse;
  1243.     cent->pe.legs.pitchAngle = 0;
  1244.     cent->pe.legs.pitching = qfalse;
  1245.  
  1246.     memset( ¢->pe.torso, 0, sizeof( cent->pe.legs ) );
  1247.     cent->pe.torso.yawAngle = cent->rawAngles[YAW];
  1248.     cent->pe.torso.yawing = qfalse;
  1249.     cent->pe.torso.pitchAngle = cent->rawAngles[PITCH];
  1250.     cent->pe.torso.pitching = qfalse;
  1251. }
  1252.  
  1253. /*
  1254. =================
  1255. CG_ProcessIdentityItems
  1256.  
  1257. Attaches all the items for a character skin
  1258. =================
  1259. */
  1260. static void CG_ProcessIdentityItems ( TGhoul2 ghoul2, TInventoryTemplate *items )
  1261. {
  1262.     TSurfaceList    *surf;
  1263.  
  1264.     while ( items )
  1265.     {
  1266.         if ( items->mItem )
  1267.         {
  1268.             if ( items->mItem->mModel && items->mBolt )
  1269.             {
  1270.                 items->mModelIndex = trap_G2API_InitGhoul2Model(&ghoul2, items->mItem->mModel, 0, 0, 0, 0, 0);
  1271.                 if (items->mModelIndex != -1)
  1272.                 {
  1273.                     items->mBoltIndex = trap_G2API_AddBolt ( ghoul2, 0, items->mBolt);
  1274.                     if (items->mBoltIndex != -1)
  1275.                     {
  1276.                         trap_G2API_AttachG2Model( ghoul2, items->mModelIndex, ghoul2, items->mBoltIndex, 0);
  1277.                     }
  1278.                 }
  1279.             }
  1280.  
  1281.             surf = items->mItem->mOffList;
  1282.             while(surf)
  1283.             {
  1284.                 trap_G2API_SetSurfaceOnOff( ghoul2, 0, surf->mName, G2SURFACEFLAG_OFF);
  1285.  
  1286.                 surf = surf->mNext;
  1287.             }
  1288.  
  1289.             surf = items->mItem->mOnList;
  1290.             while(surf)
  1291.             {
  1292.                 trap_G2API_SetSurfaceOnOff( ghoul2, 0, surf->mName, 0);
  1293.  
  1294.                 surf = surf->mNext;
  1295.             }
  1296.         }
  1297.  
  1298.         items = items->mNext;
  1299.     }
  1300. }
  1301.  
  1302. /*
  1303. =================
  1304. CG_RemoveIdentityItemsOnBack
  1305.  
  1306. Removes all identity items on the back of the given entity. There is
  1307. currently no way to get the identity items back after removing them
  1308. without killing off the player.
  1309. =================
  1310. */
  1311. void CG_RemoveIdentityItemsOnBack ( centity_t* cent )
  1312. {
  1313.     TInventoryTemplate*    items[2];
  1314.     TIdentity*            identity;
  1315.     int                    pass;
  1316.  
  1317.     // Grab the identity structure for this entity
  1318.     identity = cgs.clientinfo[cent->currentState.number].identity;
  1319.     if ( !identity )
  1320.     {
  1321.         return;
  1322.     }
  1323.  
  1324.     // Takes two passes to remove em all since the item lists are in two places
  1325.     items[0] = identity->mCharacter->mInventory;
  1326.     items[1] = identity->mSkin->mInventory;
  1327.  
  1328.     for ( pass = 0; pass < 2; pass ++ )
  1329.     {
  1330.         while ( items[pass] )
  1331.         {
  1332.             if ( items[pass]->mOnBack && items[pass]->mItem )
  1333.             {
  1334.                 TSurfaceList    *surf;
  1335.  
  1336.                 // Bolt on?
  1337.                 if ( items[pass]->mBolt && *items[pass]->mBolt)
  1338.                 {
  1339.                     trap_G2API_DetachG2Model ( cent->ghoul2, items[pass]->mModelIndex );
  1340.                     trap_G2API_RemoveGhoul2Model ( ¢->ghoul2, items[pass]->mModelIndex );
  1341.                 }
  1342.  
  1343.                 // Surface list?  Reversed from what process does
  1344.                 surf = items[pass]->mItem->mOffList;
  1345.                 while(surf)
  1346.                 {
  1347.                     trap_G2API_SetSurfaceOnOff( cent->ghoul2, 0, surf->mName, 0);
  1348.  
  1349.                     surf = surf->mNext;
  1350.                 }
  1351.  
  1352.                 surf = items[pass]->mItem->mOnList;
  1353.                 while(surf)
  1354.                 {
  1355.                     trap_G2API_SetSurfaceOnOff( cent->ghoul2, 0, surf->mName, G2SURFACEFLAG_OFF);
  1356.  
  1357.                     surf = surf->mNext;
  1358.                 }
  1359.             }
  1360.  
  1361.             items[pass] = items[pass]->mNext;
  1362.         }
  1363.     }
  1364. }
  1365.  
  1366. /*
  1367. =================
  1368. CG_RegisterIdentity
  1369.  
  1370. Registers an identity
  1371. =================
  1372. */
  1373. TGhoul2 CG_RegisterIdentity ( TIdentity* identity, char *animationFile, gender_t* gender )
  1374. {
  1375.     char                name[MAX_QPATH];
  1376.     TGenericParser2        skinFile;
  1377.     TGPGroup            *basegroup, *group, *sub;
  1378.     char                temp[20480], *end;
  1379.     int                    numPairs;
  1380.     TGhoul2                ghoul2Ptr;
  1381.  
  1382.     numPairs   = 0;
  1383.     end           = temp;
  1384.     *end       = 0;
  1385.     ghoul2Ptr  = NULL;
  1386.  
  1387.     *animationFile = 0;
  1388.  
  1389.     if (!identity )
  1390.     {
  1391.         return NULL;
  1392.     }
  1393.  
  1394.     if (trap_G2API_InitGhoul2Model( &ghoul2Ptr, identity->mCharacter->mModel, 0, 0, 0, (1<<4), 0) == -1)
  1395.     {
  1396.         return NULL;
  1397.     }
  1398.     
  1399.     trap_G2API_GetAnimFileNameIndex( ghoul2Ptr, 0, name );
  1400.     Com_sprintf(animationFile, MAX_QPATH, "%s_mp.cfg", name );
  1401.  
  1402.     if ( identity->mCharacter->mParent)
  1403.     {
  1404.         CG_ProcessIdentityItems( ghoul2Ptr, identity->mCharacter->mParent->mInventory );
  1405.     }
  1406.  
  1407.     CG_ProcessIdentityItems( ghoul2Ptr, identity->mCharacter->mInventory);
  1408.     CG_ProcessIdentityItems( ghoul2Ptr, identity->mSkin->mInventory);
  1409.  
  1410.     // don't need the mouth
  1411.     trap_G2API_SetSurfaceOnOff( ghoul2Ptr, 0, "mouth_r", G2SURFACEFLAG_OFF|G2SURFACEFLAG_NODESCENDANTS);
  1412.     trap_G2API_SetSurfaceOnOff( ghoul2Ptr, 0, "mouth_l", G2SURFACEFLAG_OFF|G2SURFACEFLAG_NODESCENDANTS);
  1413.  
  1414.     // Parse the g2skin file
  1415.     Com_sprintf( name, sizeof(name), "models/characters/skins/%s.g2skin", identity->mSkin->mSkin );
  1416.     skinFile = trap_GP_ParseFile( name, qtrue, qfalse );
  1417.     if ( !skinFile )
  1418.     {
  1419.         trap_G2API_CleanGhoul2Models( &ghoul2Ptr);
  1420.         return NULL;
  1421.     }
  1422.  
  1423.     basegroup = trap_GP_GetBaseParseGroup ( skinFile );
  1424.     group = trap_GPG_GetSubGroups ( basegroup );
  1425.  
  1426.     while(group)
  1427.     {
  1428.         trap_GPG_GetName ( group, name );
  1429.  
  1430.         // Parse the material
  1431.         if ( Q_stricmp ( name, "material") == 0)
  1432.         {
  1433.             char    matName[MAX_QPATH];
  1434.             char    shaderName[MAX_QPATH];
  1435.  
  1436.             trap_GPG_FindPairValue ( group, "name", "", matName );
  1437.  
  1438.             sub = trap_GPG_FindSubGroup ( group, "group");
  1439.             if (sub)
  1440.             {
  1441.                 trap_GPG_FindPairValue ( sub, "shader1", "", shaderName );
  1442.                 if (!shaderName[0])
  1443.                 {
  1444.                     trap_GPG_FindPairValue ( sub, "texture1", "", shaderName );
  1445.                 }
  1446.             }
  1447.  
  1448.             if (matName[0] && shaderName[0])
  1449.             {
  1450.                 end += Com_sprintf(end, sizeof(temp) - (end-temp+1), "%s %s ", matName, shaderName);
  1451.                 numPairs++;
  1452.             }
  1453.         }
  1454.  
  1455.         group = trap_GPG_GetNext ( group );
  1456.     }
  1457.  
  1458.     trap_GP_Delete(&skinFile);
  1459.  
  1460.     if (numPairs)
  1461.     {
  1462.         qhandle_t    handle;
  1463.  
  1464.         handle = trap_G2API_RegisterSkin( identity->mName, numPairs, temp);
  1465.         trap_G2API_SetSkin( ghoul2Ptr, 0, handle);
  1466.     }
  1467.  
  1468.     // If the gender was requested then look for female in the model
  1469.     // name as the indicator
  1470.     if ( gender )
  1471.     {
  1472.         Q_strlwr ( (char*)identity->mCharacter->mModel );
  1473.         
  1474.         *gender = GENDER_MALE;
  1475.         if ( strstr ( identity->mCharacter->mModel, "female" ) )
  1476.         {
  1477.             *gender = GENDER_FEMALE;
  1478.         }
  1479.     }
  1480.  
  1481.     return ghoul2Ptr;
  1482. }
  1483.